package gov.house.louise.gmg;

import java.awt.Dimension;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.MatchResult;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.filechooser.FileFilter;

import au.com.bytecode.opencsv.CSVReader;
import de.micromata.opengis.kml.v_2_2_0.Document;
import de.micromata.opengis.kml.v_2_2_0.Feature;
import de.micromata.opengis.kml.v_2_2_0.Kml;
import de.micromata.opengis.kml.v_2_2_0.Placemark;
import de.micromata.opengis.kml.v_2_2_0.Point;

public class GoogleMapsGenerator {

	private Kml kml;
	private Document doc;
	private JFrame frame;
	private List<Recipient> recipients;
	private Geocoder geocoder;

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		JFrame frame = new JFrame();
		GoogleMapsGenerator gmg = new GoogleMapsGenerator(frame);
		JFileChooser fc = new JFileChooser();
		fc.setAcceptAllFileFilterUsed(false);
		fc.addChoosableFileFilter(new FileFilter() {
			public boolean accept(File f) {
				return (f.isDirectory() || f.getName().endsWith(".csv") || f
						.getName().endsWith(".kml"));
			}

			public String getDescription() {
				return "KML, CSV Files (*.kml, *.csv)";
			}
		});

		int returnVal;
		do {
			returnVal = fc.showOpenDialog(frame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				gmg.importFile(fc.getSelectedFile());
			}
		} while (JOptionPane.showConfirmDialog(frame,
				"Would you like to add another file?", "Add File?",
				JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION);
		gmg.addRecipientsToKml();
		gmg.exportFile();
		frame.dispose();
	}

	public GoogleMapsGenerator(JFrame frame) {
		this.kml = new Kml();
		this.doc = kml.createAndSetDocument().withName("Recovery Projects");
		this.recipients = new ArrayList<Recipient>();
		this.frame = frame;
		this.geocoder = new Geocoder();
		if (this.geocoder.getKey() == null || this.geocoder.getKey().equals("")) {
			String geocoderKey = JOptionPane.showInputDialog(frame,
					"Please insert your Google Maps API Key");
			this.geocoder.setKey(geocoderKey);
			this.geocoder.saveCache(new File("geocache.tmp"));
		}
		System.out.println(this.geocoder.getKey());
		// new Geocoder(geocoderKey);
	}

	public void importFile(File src) {
		if (src.getName().endsWith(".kml")) {
			// KLM import
			Kml kmlimport = Kml.unmarshal(src);
			Document docimport = (Document) kmlimport.getFeature();
			List<Feature> placemarks = docimport.getFeature();
			String kmlDescription;
			String kmlName;
			double latitude;
			double longitude;
			String recipientName;
			String street;
			String city;
			String state;
			String zip;
			double amount;
			String strAmount;
			String awardDescription;
			String projectName;
			String projectDescription;

			Pattern pattern;
			Matcher matcher;
			MatchResult result;

			for (Feature feature : placemarks) {
				latitude = ((Point) ((Placemark) feature).getGeometry())
						.getCoordinates().get(0).getLatitude();
				longitude = ((Point) ((Placemark) feature).getGeometry())
						.getCoordinates().get(0).getLongitude();
				kmlName = ((Placemark) feature).getName();
				pattern = Pattern.compile("<span id=\"gmgName\">(.*?)</span>");
				// pattern = Pattern
				// .compile("span");
				matcher = pattern.matcher(kmlName);
				while (matcher.find()) {
					System.out.println(matcher.group(1));
				}

				// Parse name
				kmlDescription = ((Placemark) feature).getDescription();
				// Parse description into components
				// try {
				// amount = Double.parseDouble(strAmount);
				// } catch (NumberFormatException e) {
				// amount = -1;
				// }
				// this.doc.getFeature().add(feature);

				// addAward(recipientName, latitude, longitude, street, city,
				// state, zip, amount, awardDescription, projectName,
				// projectDescription);
			}
		} else if (src.getName().endsWith(".csv")) {
			// CSV import
			try {
				System.out.println("Reading CSV");
				CSVReader reader = new CSVReader(new FileReader(src
						.getCanonicalPath()));
				List<String[]> lines = reader.readAll();
				String[] headers = lines.get(0);
				lines.remove(0);
				int recipientIndex = 0, addressIndex = 0, cityIndex = 0, stateIndex = 0, zipIndex = 0, amountIndex = 0, projectDescriptionIndex = 0, projectNameIndex = 0, awardDescriptionIndex = 0;

				// Prompt for heading assignments

				int i = 0;
				for (String header : headers) {
					if (header.equals("recipient_name")) {
						recipientIndex = i;
					} else if (header.equals("project_name")) {
						projectNameIndex = i;
					} else if (header.equals("project_description")) {
						projectDescriptionIndex = i;
					} else if (header.equals("award_description")) {
						awardDescriptionIndex = i;
					} else if (header.equals("award_amount")) {
						amountIndex = i;
					} else if (header.equals("pop_st_address_1")) {
						addressIndex = i;
					} else if (header.equals("pop_city")) {
						cityIndex = i;
					} else if (header.equals("pop_state_cd")) {
						stateIndex = i;
					} else if (header.equals("pop_postal_cd")) {
						zipIndex = i;
					}
					i++;
				}
				System.out.println("Headers read");
				int j = 0;
				double[] latLon;
				JProgressBar progBar = new JProgressBar();
				this.frame.add(progBar);
				this.frame.setSize(200, 50);
				Dimension dim = frame.getToolkit().getScreenSize();
				frame.setLocation((int) (dim.getWidth() / 2) - 100, (int) (dim
						.getHeight() / 2) - 25);
				this.frame.setVisible(true);
				progBar.setSize(200, 50);
				progBar.setMaximum(lines.size());
				progBar.setValue(0);
				for (String[] line : lines) {
					// if (j > 20) {
					// break;
					// }
					j++;
					// System.out.println(line[descriptionIndex]);

					// Look up LatLon
					latLon = this.geocoder.LookupAddress(line[addressIndex],
							line[cityIndex], line[stateIndex], line[zipIndex]);
					double dblAmount;
					if (latLon != null) {
						try {
							dblAmount = Double.parseDouble(line[amountIndex]);
						} catch (NumberFormatException e) {
							dblAmount = -1;
						}
						if ((int) latLon[0] != 0 && (int) latLon[1] != 0) {
							addAward(line[recipientIndex], latLon[0],
									latLon[1], line[addressIndex],
									line[cityIndex], line[stateIndex],
									line[zipIndex], dblAmount,
									line[awardDescriptionIndex],
									line[projectNameIndex],
									line[projectDescriptionIndex]);
						}
					}
					progBar.setValue(j);
				}
				this.geocoder.saveCache(new File("geocache.tmp"));
				frame.remove(progBar);
				frame.setVisible(false);
				System.out.println("Output complete");
			} catch (FileNotFoundException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void addAward(String recipientName, double latitude,
			double longitude, String street, String city, String state,
			String zip, double amount, String awardDescription,
			String projectName, String projectDescription) {
		Recipient recipient = null;
		for (Recipient r : this.recipients) {
			if (r.getName().equals(recipientName)
					&& r.getLatitude() == latitude
					&& r.getLongitude() == longitude) {
				recipient = r;
				break;
			}
		}
		if (recipient == null) {
			recipient = new Recipient(recipientName);
			recipient.setLatitude(latitude);
			recipient.setLongitude(longitude);
			recipients.add(recipient);
		}

		RecipientAddress address = new RecipientAddress(street, city, state,
				zip);
		recipient.setAddress(address);

		Award award = new Award();
		award.setAmount(amount);
		award.setAwardDescription(awardDescription);
		award.setProjectDescription(projectDescription);
		award.setProjectName(projectName);
		recipient.getAwards().add(award);
	}

	public void addRecipientsToKml() {
		JProgressBar progBar = new JProgressBar();
		this.frame.add(progBar);
		this.frame.setSize(200, 50);
		Dimension dim = frame.getToolkit().getScreenSize();
		frame.setLocation((int) (dim.getWidth() / 2) - 100, (int) (dim
				.getHeight() / 2) - 25);
		this.frame.setVisible(true);
		progBar.setSize(200, 50);
		progBar.setMaximum(this.recipients.size());
		progBar.setValue(0);

		StringBuilder sb;
		Placemark p;

		int i = 0;
		for (Recipient r : this.recipients) {
			sb = new StringBuilder();
			// sb.append("<b><span id=\"gmgName\">");
			// sb.append(r.getName());
			// sb.append("</span></b>");
			for (Award a : r.getAwards()) {
				// sb.append("<br/><br/>");
				sb.append("<b>Project Name:</b> ");
				sb.append("<span class=\"gmgProjectName\">");
				sb.append(a.getProjectName());
				sb.append("</span><br/>");
				// sb.append("<b>Project Description:</b> ");
				// sb.append("<span class=\"gmgProjectDescription\">");
				// sb.append(a.getProjectDescription());
				// sb.append("</span><br/>");
				if (a.getAmount() == -1) {
					sb.append("<b>Amount Awarded:</b> Unspecified<br/>");
				} else {
					sb.append("<b>Amount Awarded:</b> ");
					sb.append("$<span class=\"gmgAmount\">");
					sb.append(String.format("%,.2f", a.getAmount()));
					sb.append("</span><br/>");
				}
				sb.append("<b>Award Description:</b> ");
				sb.append("<span class=\"gmgAwardDescription\">");
				sb.append(a.getAwardDescription());
				sb.append("</span><br/><br/>");
			}
			p = doc.createAndAddPlacemark();
			if (r.getAwards().size() > 1) {
				p.setName("<b><span id=\"gmgName\">" + r.getName()
						+ "</span> (" + r.getAwards().size() + " awards)</b>");
			} else {
				p.setName("<b><span id=\"gmgName\">" + r.getName()
						+ "</span> (" + r.getAwards().size() + " award)</b>");

			}
			p.setDescription(sb.toString());
			p.createAndSetPoint().addToCoordinates(r.getLongitude(),
					r.getLatitude());
			i++;
			progBar.setValue(i);
		}

		frame.remove(progBar);
		frame.setVisible(false);

	}

	public void exportFile() {
		try {
			JFileChooser fc = new JFileChooser();
			// fc.setSelectedFile(new File("projects.kml"));
			int returnVal;
			boolean marshalVal = false;
			returnVal = fc.showSaveDialog(frame);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				marshalVal = this.kml.marshal(fc.getSelectedFile());
			} else {
				System.out.println("Quitting without saving");
			}
			if (marshalVal) {
				JOptionPane.showMessageDialog(this.frame,
						"KML Saved Successfully!");
			} else {
				JOptionPane.showMessageDialog(this.frame,
						"There was a problem saving KML!");
			}

		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			// Marshalling should create the file, so this should never happen
			e.printStackTrace();
		}
	}

	public JFrame getFrame() {
		return frame;
	}

	public void setFrame(JFrame frame) {
		this.frame = frame;
	}

	public void setGeocoder(Geocoder geocoder) {
		this.geocoder = geocoder;
	}

	public Geocoder getGeocoder() {
		return geocoder;
	}

	private class Recipient {
		private String name;
		private RecipientAddress address;
		private double latitude;
		private double longitude;
		private ArrayList<Award> awards;

		public Recipient(String name) {
			this.name = name;
			this.setAwards(new ArrayList<Award>());
		}

		public void setName(String name) {
			this.name = name;
		}

		public String getName() {
			return name;
		}

		public void setAddress(RecipientAddress address) {
			this.address = address;
		}

		public RecipientAddress getAddress() {
			return address;
		}

		public void setLatitude(double latitude) {
			this.latitude = latitude;
		}

		public double getLatitude() {
			return latitude;
		}

		public void setLongitude(double longitude) {
			this.longitude = longitude;
		}

		public double getLongitude() {
			return longitude;
		}

		public void setAwards(ArrayList<Award> awards) {
			this.awards = awards;
		}

		public ArrayList<Award> getAwards() {
			return awards;
		}
	}

	private class RecipientAddress {

		private String street;
		private String city;
		private String state;
		private String zip;

		public RecipientAddress(String street, String city, String state,
				String zip) {
			super();
			this.street = street;
			this.city = city;
			this.state = state;
			this.zip = zip;
		}

		public String getStreet() {
			return street;
		}

		public void setStreet(String street) {
			this.street = street;
		}

		public String getCity() {
			return city;
		}

		public void setCity(String city) {
			this.city = city;
		}

		public String getState() {
			return state;
		}

		public void setState(String state) {
			this.state = state;
		}

		public String getZip() {
			return zip;
		}

		public void setZip(String zip) {
			this.zip = zip;
		}
	}

	private class Award {
		private double amount;
		private String projectName;
		private String projectDescription;
		private String awardDescription;

		public void setAmount(double amount) {
			this.amount = amount;
		}

		public double getAmount() {
			return amount;
		}

		public void setAwardDescription(String awardDescription) {
			this.awardDescription = awardDescription;
		}

		public String getAwardDescription() {
			return awardDescription;
		}

		public void setProjectDescription(String projectDescription) {
			this.projectDescription = projectDescription;
		}

		public String getProjectDescription() {
			return projectDescription;
		}

		public void setProjectName(String projectName) {
			this.projectName = projectName;
		}

		public String getProjectName() {
			return projectName;
		}
	}
}
